import * as React from 'react';
import { Route, Router, Switch, Redirect } from 'react-router';
import dynamic from '../dva/dynamic';
import { ILoadModelFun, IRouterConfigProps } from './interface';
import { WxApp } from '@/interfaces';

function checkRouteItem(route: WxApp.RouteItem) {
  route.modelPath = route.modelPath || [];
  route.models = route.models || [];
  route.exact = route.exact || false;
  route.children = route.children || [];
  route.wrapped = route.wrapped || false;
}

function getRoute(
  publicModels: string[],
  routeItem: WxApp.RouteItem,
  parent: WxApp.RouteItem | null,
  routeData: WxApp.RouteItem[],
  loadModel: ILoadModelFun
) {
  checkRouteItem(routeItem);
  routeItem.modelPath = routeItem.modelPath || [];
  if (parent) {
    checkRouteItem(parent);
    parent.modelPath = parent.modelPath || [];
    parent.models = parent.models || [];
    routeItem.path = (parent.path + '/' + routeItem.path).replace(/\/{2,3}/g, '/');
    if (!routeItem.component) {
      routeItem.component = parent.component;
      routeItem.modelPath = [...parent.modelPath, ...routeItem.modelPath];
    }
  }
  const { path, exact, modelPath = [], component } = routeItem;
  routeData.push({
    path,
    exact,
    models: () => publicModels.concat(modelPath).map(loadModel),
    component,
  });
}

function getRoutes(
  publicModels: string[],
  routes: WxApp.RouteItem[],
  routeData: WxApp.RouteItem[],
  parent: WxApp.RouteItem | null = null,
  loadModel: ILoadModelFun
) {
  const defaults = {
    exact: true,
    models: [],
    children: [],
    wrapped: false,
    path: '/',
  };
  routes.forEach((paramRoute) => {
    const route: WxApp.RouteItem = { ...defaults, ...paramRoute };
    getRoute(publicModels, route, parent, routeData, loadModel);
    if (route.children && route.children.length) {
      getRoutes(publicModels, route.children, routeData, route, loadModel);
    }
  });
  return routeData;
}

export default function (
  routes: WxApp.RouteObject[],
  publicModels: string[] = [],
  loadModel: ILoadModelFun,
  hooks?: Function[],
  root?: Function
) {
  return function RootRoute({ history, app, fallbackRoute = '/' }: IRouterConfigProps) {
    const routeData: WxApp.RouteItem[] = [];
    getRoutes(publicModels, routes, routeData, null, loadModel);
    if (hooks) {
      hooks.forEach((hook) => {
        hook(routeData);
      });
    }
    const RouteRoot = (
      <Router history={history}>
        <Switch>
          {routeData.map(({ path, exact, ...dynamics }, key) => (
            <Route exact={exact} key={key} path={path} component={dynamic({ app, ...dynamics } as any) as any} />
          ))}
          {fallbackRoute && <Redirect to={fallbackRoute} />}
        </Switch>
      </Router>
    );
    return root ? root(RouteRoot) : RouteRoot;
  };
}
